package com.syeet.gulimall.search.thread;

import java.util.concurrent.*;

/**
 * 异步编排
 * 业务场景：
 * 查询商品详情页的逻辑比较复杂，有些数据还需要远程调用，必然需要花费更多的时间。
 * 1 获取sku基本信息 0.5
 * 2 获取sku图片信息 0.5
 * 获取sku促销信息 1
 * spu所有销售属性 1
 * 获取规格参数 1.5
 * spu详情 1
 * 假如商品详情页的每个查询，需要如下标注的时间才能完成
 * 那么，用户需要5.5s后才能看到商品详情页的内容。很显然是不能接受的。
 * 如果有多个线程同时完成这6步操作，也许只需要1.5s即可完成响应。
 **/
public class ThreadTest {

    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // System.out.println("main......start.....");
        // Thread thread = new Thread01();
        // thread.start();
        // System.out.println("main......end.....");

        // Runable01 runable01 = new Runable01();
        // new Thread(runable01).start();

        // FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
        // new Thread(futureTask).start();
        // System.out.println(futureTask.get());

        // service.execute(new Runable01());
        // Future<Integer> submit = service.submit(new Callable01());
        // submit.get();

        /**
         * 1、runXxxx 都是没有返回结果的，supplyXxx 都是可以获取返回结果的(下面的future.get())
         * get方法阻塞或者轮询的方式获得结果，但是这种方式不推荐使用
         * 2、可以传入自定义的线程池，否则就用默认的线程池；
         */

        // CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        //     System.out.println("当前线程：" + Thread.currentThread().getId());
        //     int i = 10 / 2;
        //     System.out.println("运行结果：" + i);
        // }, executor);


        /**
         * 计算完成时回调方法
         * whenComplete 可以处理正常和异常的计算结果，exceptionally 处理异常情况。
         * whenComplete 和 whenCompleteAsync 的区别：
         * whenComplete：是执行当前任务的线程继续执行 whenComplete 的任务。
         * whenCompleteAsync：是执行当前任务的线程提交给线程池 至于whenCompleteAsync任务由线程池
         * 来决定哪个线程执行。（有可能恰好还是当前线程继续执行）
         * 方法不以 Async 结尾，意味着 Action 使用相同的线程执行，而 Async 可能会使用其他线程
         * 执行（如果是使用相同的线程池，也可能会被同一个线程选中执行）
         * whenCompleteAsync(T t,U u) 参数t表示结果 u表示异常
         */

        // CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //     System.out.println("当前线程：" + Thread.currentThread().getId());
        //     int i = 10 / 0;
        //     System.out.println("运行结果：" + i);
        //     return i;
        // }, executor).whenCompleteAsync((res,exception) -> {
        //     //虽然能得到异常信息，但是没法修改返回数据
        //     System.out.println("异步任务成功完成了...结果是：" + res + "异常是：" + exception);
        // }).exceptionally(throwable -> {
        //     //可以感知异常，同时返回默认值
        //     return 10;
        // });


        /**
         * handle方法
         * 和complete一样，可对结果做最后的处理（可处理异常），可改变返回值。
         */

        // CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        //     System.out.println("当前线程：" + Thread.currentThread().getId());
        //     int i = 10 / 2;
        //     System.out.println("运行结果：" + i);
        //     return i;
        // }, executor).handle((result,thr) -> {
        //     if (result != null) {
        //         return result * 2;
        //     }
        //     if (thr != null) {
        //         System.out.println("异步任务成功完成了,有异常...结果是：" + result + "异常是：" + thr);
        //         return 0;
        //     }
        //
        // });


        /**
         * 线程串行化
         * 1、thenRun：只要上面的任务执行完成，就开始执行thenRun，只是处理完任务后，执行thenRun的后续操作
         * 并不能获取上一步的执行结果
         * 2、thenAcceptAsync：能接受上一步结果，但是无返回值
         * 3、thenApplyAsync：能接受上一步结果，有返回值
         *带有Async默认是异步执行的
         */

//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executor).thenApplyAsync(res -> {
//            System.out.println("任务2启动了..." + res);
//            return "Hello" + res;
//        }, executor);


        /**
         * 两任务组合-都要完成
         * 两个任务必须都完成，触发该任务。
         * thenCombine：组合两个future，获取两个future的返回结果，并返回当前任务的返回值
         * thenAcceptBoth：组合两个future，获取两个future任务的返回结果，然后处理任务，没有返回值。
         * runAfterBoth：组合两个future，不需要获取future的结果，只需两个future处理完任务后，处理该任务。
         */

//        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务1开始：" + Thread.currentThread().getName());
//            int i = 10 / 2;
//            System.out.println("任务1结束：");
//            return i+"";
//        }, executor);
//
//        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {
//            System.out.println("任务2开始：" + Thread.currentThread().getName());
//            System.out.println("任务2结束：");
//            return "hello";
//        }, executor);

//        //f1,f2是任务1,2的结果值
//        CompletableFuture<String> future = future01.thenCombineAsync(future02, (f1, f2) -> {
//            return " 任务3开始：" + f1 + ":" + f2;
//        }, executor);

        /**
         * 两任务组合-一个完成
         * 当两个任务中，任意一个future任务完成的时候，执行任务。
         * applyToEither：两个任务有一个执行完成，获取它的返回值，处理任务并有新的返回值。
         * acceptEither：两个任务有一个执行完成，获取它的返回值，处理任务，没有新的返回值。
         * runAfterEither：两个任务有一个执行完成，不需要获取future的结果，处理任务，也没有返回值。
         */

//        CompletableFuture<String> future = future01.applyToEitherAsync(future02, (res) -> {
//            return " 任务3开始：" + res;
//        }, executor);


        /**
         * 多任务组合
         * allOf：等待所有任务完成
         * anyOf：只要有一个任务完成
         */
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("商品图片");
            return "hello.jpg";
        }, executor);
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("商品属性");
            return "黑色+256G";
        }, executor);
        CompletableFuture<String> futureIntr = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("商品介绍");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "华为";
        }, executor);
        CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureIntr);
        allOf.join();//等待所有结果执行

       // System.out.println("当前线程：" + Thread.currentThread().getName() + ",获取的值：" + future.get());

    }

    private static void threadPool() {
        /**
         * corePoolSize 核心线程数
         * maximumPoolSize 最大线程数
         * keepAliveTime 当线程数大于核心线程数的时候，线程在最大多长时间没有接到新任务就会终止释放，
         * 最终线程池维持在 corePoolSize 大小
         * unit 时间单位
         * workQueue 阻塞队列，用来存储等待执行的任务，如果当前对线程的需求超过了 corePoolSize
         * 大小，就会放在这里等待空闲线程执行。
         * threadFactory 创建线程的工厂，比如指定线程名等
         * handler 拒绝策略，如果线程满了，线程池就会使用拒绝策略。
         *
         * 运行流程：
         * 1、线程池创建，准备好 core 数量的核心线程，准备接受任务
         * 2、新的任务进来，用 core 准备好的空闲线程执行。
         * (1) 、core 满了，就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队
         * 列获取任务执行
         * (2) 、阻塞队列满了，就直接开新线程执行，最大只能开到 max 指定的数量
         * (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自
         * 动销毁。最终保持到 core 大小
         * (4) 、如果线程数开到了 max 的数量，还有新任务进来，就会使用 reject 指定的拒绝策
         * 略进行处理
         * 3、所有的线程创建都是由指定的 factory 创建的。
         *
         * 面试：
         * 一个线程池 core 7； max 20 ，queue：50，100 并发进来怎么分配的；
         * 先有 7 个能直接得到执行，接下来 50 个进入队列排队，在多开 13 个继续执行。现在 70 个
         * 被安排上了。剩下 30 个默认拒绝策略
         */
        ExecutorService threadPool = new ThreadPoolExecutor(
                200,
                10,
                10L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(10000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        //定时任务的线程池
        ExecutorService service = Executors.newScheduledThreadPool(2);
    }


    public static class Thread01 extends Thread {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }


    public static class Runable01 implements Runnable {
        @Override
        public void run() {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
        }
    }


    public static class Callable01 implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程：" + Thread.currentThread().getId());
            int i = 10 / 2;
            System.out.println("运行结果：" + i);
            return i;
        }
    }

}
